/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <algorithm>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include <variant>

// android::base::expected is an Android implementation of the std::expected
// proposal.
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html
//
// Usage:
// using android::base::expected;
// using android::base::unexpected;
//
// expected<double,std::string> safe_divide(double i, double j) {
//   if (j == 0) return unexpected("divide by zero");
//   else return i / j;
// }
//
// void test() {
//   auto q = safe_divide(10, 0);
//   if (q) { printf("%f\n", q.value()); }
//   else { printf("%s\n", q.error().c_str()); }
// }
//
// When the proposal becomes part of the standard and is implemented by
// libcxx, this will be removed and android::base::expected will be
// type alias to std::expected.
//

namespace android {
namespace base {

// Synopsis
template <class T, class E> class expected;

template <class E> class unexpected;
template <class E> unexpected(E)->unexpected<E>;

template <class E> class bad_expected_access;

template <> class bad_expected_access<void>;

struct unexpect_t {
  explicit unexpect_t() = default;
};
inline constexpr unexpect_t unexpect{};

// macros for SFINAE
#define _ENABLE_IF(...) , std::enable_if_t<(__VA_ARGS__)> * = nullptr

// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
// ignored when used as a return value. This is off by default.
#ifdef NODISCARD_EXPECTED
#define _NODISCARD_ [[nodiscard]]
#else
#define _NODISCARD_
#endif

// Class expected
template <class T, class E> class _NODISCARD_ expected {
public:
  using value_type = T;
  using error_type = E;
  using unexpected_type = unexpected<E>;

  template <class U> using rebind = expected<U, error_type>;

  // constructors
  constexpr expected() = default;
  constexpr expected(const expected &rhs) = default;
  constexpr expected(expected &&rhs) noexcept = default;

  template <class U, class G _ENABLE_IF(std::is_constructible_v<T, const U &>
                                            &&std::is_constructible_v<E, const G &> &&
                                        !std::is_constructible_v<T, expected<U, G> &> &&
                                        !std::is_constructible_v<T, expected<U, G> &&> &&
                                        !std::is_constructible_v<T, const expected<U, G> &> &&
                                        !std::is_constructible_v<T, const expected<U, G> &&> &&
                                        !std::is_convertible_v<expected<U, G> &, T> &&
                                        !std::is_convertible_v<expected<U, G> &&, T> &&
                                        !std::is_convertible_v<const expected<U, G> &, T> &&
                                        !std::is_convertible_v<const expected<U, G> &&, T> &&
                                        !(!std::is_convertible_v<const U &, T> ||
                                          !std::is_convertible_v<const G &, E>)/* non-explicit */
                                        )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr expected(const expected<U, G> &rhs) {
    if (rhs.has_value())
      var_ = rhs.value();
    else
      var_ = unexpected(rhs.error());
  }

  template <class U, class G _ENABLE_IF(std::is_constructible_v<T, const U &>
                                            &&std::is_constructible_v<E, const G &> &&
                                        !std::is_constructible_v<T, expected<U, G> &> &&
                                        !std::is_constructible_v<T, expected<U, G> &&> &&
                                        !std::is_constructible_v<T, const expected<U, G> &> &&
                                        !std::is_constructible_v<T, const expected<U, G> &&> &&
                                        !std::is_convertible_v<expected<U, G> &, T> &&
                                        !std::is_convertible_v<expected<U, G> &&, T> &&
                                        !std::is_convertible_v<const expected<U, G> &, T> &&
                                        !std::is_convertible_v<const expected<U, G> &&, T> &&
                                        (!std::is_convertible_v<const U &, T> ||
                                         !std::is_convertible_v<const G &, E>)/* explicit */
                                        )>
  constexpr explicit expected(const expected<U, G> &rhs) {
    if (rhs.has_value())
      var_ = rhs.value();
    else
      var_ = unexpected(rhs.error());
  }

  template <class U, class G _ENABLE_IF(std::is_constructible_v<T, const U &>
                                            &&std::is_constructible_v<E, const G &> &&
                                        !std::is_constructible_v<T, expected<U, G> &> &&
                                        !std::is_constructible_v<T, expected<U, G> &&> &&
                                        !std::is_constructible_v<T, const expected<U, G> &> &&
                                        !std::is_constructible_v<T, const expected<U, G> &&> &&
                                        !std::is_convertible_v<expected<U, G> &, T> &&
                                        !std::is_convertible_v<expected<U, G> &&, T> &&
                                        !std::is_convertible_v<const expected<U, G> &, T> &&
                                        !std::is_convertible_v<const expected<U, G> &&, T> &&
                                        !(!std::is_convertible_v<const U &, T> ||
                                          !std::is_convertible_v<const G &, E>)/* non-explicit */
                                        )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr expected(expected<U, G> &&rhs) {
    if (rhs.has_value())
      var_ = std::move(rhs.value());
    else
      var_ = unexpected(std::move(rhs.error()));
  }

  template <class U, class G _ENABLE_IF(std::is_constructible_v<T, const U &>
                                            &&std::is_constructible_v<E, const G &> &&
                                        !std::is_constructible_v<T, expected<U, G> &> &&
                                        !std::is_constructible_v<T, expected<U, G> &&> &&
                                        !std::is_constructible_v<T, const expected<U, G> &> &&
                                        !std::is_constructible_v<T, const expected<U, G> &&> &&
                                        !std::is_convertible_v<expected<U, G> &, T> &&
                                        !std::is_convertible_v<expected<U, G> &&, T> &&
                                        !std::is_convertible_v<const expected<U, G> &, T> &&
                                        !std::is_convertible_v<const expected<U, G> &&, T> &&
                                        (!std::is_convertible_v<const U &, T> ||
                                         !std::is_convertible_v<const G &, E>)/* explicit */
                                        )>
  constexpr explicit expected(expected<U, G> &&rhs) {
    if (rhs.has_value())
      var_ = std::move(rhs.value());
    else
      var_ = unexpected(std::move(rhs.error()));
  }

  template <class U = T _ENABLE_IF(
                std::is_constructible_v<T, U &&> &&
                !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
                !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
                !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
                std::is_convertible_v<U &&, T> /* non-explicit */
                )>
  // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
  constexpr expected(U &&v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}

  template <class U = T _ENABLE_IF(
                std::is_constructible_v<T, U &&> &&
                !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
                !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
                !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
                !std::is_convertible_v<U &&, T> /* explicit */
                )>
  // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
  constexpr explicit expected(U &&v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}

  template <class G = E _ENABLE_IF(std::is_constructible_v<E, const G &>
                                       &&std::is_convertible_v<const G &, E> /* non-explicit */
                                   )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr expected(const unexpected<G> &e) : var_(std::in_place_index<1>, e.value()) {}

  template <class G = E _ENABLE_IF(std::is_constructible_v<E, const G &> &&
                                   !std::is_convertible_v<const G &, E> /* explicit */
                                   )>
  constexpr explicit expected(const unexpected<G> &e)
      : var_(std::in_place_index<1>, E(e.value())) {}

  template <class G = E _ENABLE_IF(
                std::is_constructible_v<E, G &&> &&std::is_convertible_v<G &&, E> /* non-explicit */
                )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr expected(unexpected<G> &&e) : var_(std::in_place_index<1>, std::move(e.value())) {}

  template <class G = E _ENABLE_IF(std::is_constructible_v<E, G &&> &&
                                   !std::is_convertible_v<G &&, E> /* explicit */
                                   )>
  constexpr explicit expected(unexpected<G> &&e)
      : var_(std::in_place_index<1>, E(std::move(e.value()))) {}

  template <class... Args _ENABLE_IF(std::is_constructible_v<T, Args &&...>)>
  constexpr explicit expected(std::in_place_t, Args &&... args)
      : var_(std::in_place_index<0>, std::forward<Args>(args)...) {}

  template <class U, class... Args _ENABLE_IF(
                         std::is_constructible_v<T, std::initializer_list<U> &, Args...>)>
  constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args &&... args)
      : var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}

  template <class... Args _ENABLE_IF(std::is_constructible_v<E, Args...>)>
  constexpr explicit expected(unexpect_t, Args &&... args)
      : var_(unexpected_type(std::forward<Args>(args)...)) {}

  template <class U, class... Args _ENABLE_IF(
                         std::is_constructible_v<E, std::initializer_list<U> &, Args...>)>
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args &&... args)
      : var_(unexpected_type(il, std::forward<Args>(args)...)) {}

  // destructor
  ~expected() = default;

  // assignment
  // Note: SFNAIE doesn't work here because assignment operator should be
  // non-template. We could workaround this by defining a templated parent class
  // having the assignment operator. This incomplete implementation however
  // doesn't allow us to copy assign expected<T,E> even when T is non-copy
  // assignable. The copy assignment will fail by the underlying std::variant
  // anyway though the error message won't be clear.
  expected &operator=(const expected &rhs) = default;

  // Note for SFNAIE above applies to here as well
  expected &operator=(expected &&rhs) noexcept(
      std::is_nothrow_move_assignable_v<T> &&std::is_nothrow_move_assignable_v<E>) = default;

  template <class U = T _ENABLE_IF(
                !std::is_void_v<T> &&
                !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
                !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
                std::is_constructible_v<T, U> && std::is_assignable_v<T &, U> &&
                std::is_nothrow_move_constructible_v<E>)>
  expected &operator=(U &&rhs) {
    var_ = T(std::forward<U>(rhs));
    return *this;
  }

  template <class G = E> expected &operator=(const unexpected<G> &rhs) {
    var_ = rhs;
    return *this;
  }

  template <class G = E _ENABLE_IF(
                std::is_nothrow_move_constructible_v<G> &&std::is_move_assignable_v<G>)>
  expected &operator=(unexpected<G> &&rhs) {
    var_ = std::move(rhs);
    return *this;
  }

  // modifiers
  template <class... Args _ENABLE_IF(std::is_nothrow_constructible_v<T, Args...>)>
  T &emplace(Args &&... args) {
    expected(std::in_place, std::forward<Args>(args)...).swap(*this);
    return value();
  }

  template <class U, class... Args _ENABLE_IF(
                         std::is_nothrow_constructible_v<T, std::initializer_list<U> &, Args...>)>
  T &emplace(std::initializer_list<U> il, Args &&... args) {
    expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
    return value();
  }

  // swap
  template <typename U = T,
            typename = std::enable_if_t<(std::is_swappable_v<U> && std::is_swappable_v<E> &&
                                         (std::is_move_constructible_v<U> ||
                                          std::is_move_constructible_v<E>))>>
  void swap(expected &rhs) noexcept(
      std::is_nothrow_move_constructible_v<T> &&std::is_nothrow_swappable_v<T>
          &&std::is_nothrow_move_constructible_v<E> &&std::is_nothrow_swappable_v<E>) {
    var_.swap(rhs.var_);
  }

  // observers
  constexpr const T *operator->() const { return std::addressof(value()); }
  constexpr T *operator->() { return std::addressof(value()); }
  constexpr const T &operator*() const & { return value(); }
  constexpr T &operator*() & { return value(); }
  constexpr const T &&operator*() const && { return std::move(std::get<T>(var_)); }
  constexpr T &&operator*() && { return std::move(std::get<T>(var_)); }

  constexpr explicit operator bool() const noexcept { return has_value(); }
  constexpr bool has_value() const noexcept { return var_.index() == 0; }
  constexpr bool ok() const noexcept { return has_value(); }

  constexpr const T &value() const & { return std::get<T>(var_); }
  constexpr T &value() & { return std::get<T>(var_); }
  constexpr const T &&value() const && { return std::move(std::get<T>(var_)); }
  constexpr T &&value() && { return std::move(std::get<T>(var_)); }

  constexpr const E &error() const & { return std::get<unexpected_type>(var_).value(); }
  constexpr E &error() & { return std::get<unexpected_type>(var_).value(); }
  constexpr const E &&error() const && {
    return std::move(std::get<unexpected_type>(var_)).value();
  }
  constexpr E &&error() && { return std::move(std::get<unexpected_type>(var_)).value(); }

  template <class U _ENABLE_IF(std::is_copy_constructible_v<T> &&std::is_convertible_v<U, T>)>
  constexpr T value_or(U &&v) const & {
    if (has_value())
      return value();
    else
      return static_cast<T>(std::forward<U>(v));
  }

  template <class U _ENABLE_IF(std::is_move_constructible_v<T> &&std::is_convertible_v<U, T>)>
  constexpr T value_or(U &&v) && {
    if (has_value())
      return std::move(value());
    else
      return static_cast<T>(std::forward<U>(v));
  }

  // expected equality operators
  template <class T1, class E1, class T2, class E2>
  friend constexpr bool operator==(const expected<T1, E1> &x, const expected<T2, E2> &y);
  template <class T1, class E1, class T2, class E2>
  friend constexpr bool operator!=(const expected<T1, E1> &x, const expected<T2, E2> &y);

  // Comparison with unexpected<E>
  template <class T1, class E1, class E2>
  friend constexpr bool operator==(const expected<T1, E1> &, const unexpected<E2> &);
  template <class T1, class E1, class E2>
  friend constexpr bool operator==(const unexpected<E2> &, const expected<T1, E1> &);
  template <class T1, class E1, class E2>
  friend constexpr bool operator!=(const expected<T1, E1> &, const unexpected<E2> &);
  template <class T1, class E1, class E2>
  friend constexpr bool operator!=(const unexpected<E2> &, const expected<T1, E1> &);

  // Specialized algorithms
  template <class T1, class E1> friend void swap(expected<T1, E1> &, expected<T1, E1> &) noexcept;

private:
  std::variant<value_type, unexpected_type> var_;
};

template <class T1, class E1, class T2, class E2>
constexpr bool operator==(const expected<T1, E1> &x, const expected<T2, E2> &y) {
  if (x.has_value() != y.has_value())
    return false;
  if (!x.has_value())
    return x.error() == y.error();
  return *x == *y;
}

template <class T1, class E1, class T2, class E2>
constexpr bool operator!=(const expected<T1, E1> &x, const expected<T2, E2> &y) {
  return !(x == y);
}

// Comparison with unexpected<E>
template <class T1, class E1, class E2>
constexpr bool operator==(const expected<T1, E1> &x, const unexpected<E2> &y) {
  return !x.has_value() && (x.error() == y.value());
}
template <class T1, class E1, class E2>
constexpr bool operator==(const unexpected<E2> &x, const expected<T1, E1> &y) {
  return !y.has_value() && (x.value() == y.error());
}
template <class T1, class E1, class E2>
constexpr bool operator!=(const expected<T1, E1> &x, const unexpected<E2> &y) {
  return x.has_value() || (x.error() != y.value());
}
template <class T1, class E1, class E2>
constexpr bool operator!=(const unexpected<E2> &x, const expected<T1, E1> &y) {
  return y.has_value() || (x.value() != y.error());
}

template <class E> class _NODISCARD_ expected<void, E> {
public:
  using value_type = void;
  using error_type = E;
  using unexpected_type = unexpected<E>;

  // constructors
  constexpr expected() = default;
  constexpr expected(const expected &rhs) = default;
  constexpr expected(expected &&rhs) noexcept = default;

  template <class U, class G _ENABLE_IF(
                         std::is_void_v<U> &&std::is_convertible_v<const G &, E> /* non-explicit */
                         )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr expected(const expected<U, G> &rhs) {
    if (!rhs.has_value())
      var_ = unexpected(rhs.error());
  }

  template <class U, class G _ENABLE_IF(std::is_void_v<U> &&
                                        !std::is_convertible_v<const G &, E> /* explicit */
                                        )>
  constexpr explicit expected(const expected<U, G> &rhs) {
    if (!rhs.has_value())
      var_ = unexpected(rhs.error());
  }

  template <class U, class G _ENABLE_IF(
                         std::is_void_v<U> &&std::is_convertible_v<const G &&, E> /* non-explicit */
                         )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr expected(expected<U, G> &&rhs) {
    if (!rhs.has_value())
      var_ = unexpected(std::move(rhs.error()));
  }

  template <class U, class G _ENABLE_IF(std::is_void_v<U> &&
                                        !std::is_convertible_v<const G &&, E> /* explicit */
                                        )>
  constexpr explicit expected(expected<U, G> &&rhs) {
    if (!rhs.has_value())
      var_ = unexpected(std::move(rhs.error()));
  }

  template <class G = E _ENABLE_IF(std::is_constructible_v<E, const G &>
                                       &&std::is_convertible_v<const G &, E> /* non-explicit */
                                   )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr expected(const unexpected<G> &e) : var_(std::in_place_index<1>, e.value()) {}

  template <class G = E _ENABLE_IF(std::is_constructible_v<E, const G &> &&
                                   !std::is_convertible_v<const G &, E> /* explicit */
                                   )>
  constexpr explicit expected(const unexpected<G> &e)
      : var_(std::in_place_index<1>, E(e.value())) {}

  template <class G = E _ENABLE_IF(
                std::is_constructible_v<E, G &&> &&std::is_convertible_v<G &&, E> /* non-explicit */
                )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr expected(unexpected<G> &&e) : var_(std::in_place_index<1>, std::move(e.value())) {}

  template <class G = E _ENABLE_IF(std::is_constructible_v<E, G &&> &&
                                   !std::is_convertible_v<G &&, E> /* explicit */
                                   )>
  constexpr explicit expected(unexpected<G> &&e)
      : var_(std::in_place_index<1>, E(std::move(e.value()))) {}

  template <class... Args _ENABLE_IF(sizeof...(Args) == 0)>
  constexpr explicit expected(std::in_place_t, Args &&...) {}

  template <class... Args _ENABLE_IF(std::is_constructible_v<E, Args...>)>
  constexpr explicit expected(unexpect_t, Args &&... args)
      : var_(unexpected_type(std::forward<Args>(args)...)) {}

  template <class U, class... Args _ENABLE_IF(
                         std::is_constructible_v<E, std::initializer_list<U> &, Args...>)>
  constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args &&... args)
      : var_(unexpected_type(il, std::forward<Args>(args)...)) {}

  // destructor
  ~expected() = default;

  // assignment
  // Note: SFNAIE doesn't work here because assignment operator should be
  // non-template. We could workaround this by defining a templated parent class
  // having the assignment operator. This incomplete implementation however
  // doesn't allow us to copy assign expected<T,E> even when T is non-copy
  // assignable. The copy assignment will fail by the underlying std::variant
  // anyway though the error message won't be clear.
  expected &operator=(const expected &rhs) = default;

  // Note for SFNAIE above applies to here as well
  expected &operator=(expected &&rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;

  template <class G = E> expected &operator=(const unexpected<G> &rhs) {
    var_ = rhs;
    return *this;
  }

  template <class G = E _ENABLE_IF(
                std::is_nothrow_move_constructible_v<G> &&std::is_move_assignable_v<G>)>
  expected &operator=(unexpected<G> &&rhs) {
    var_ = std::move(rhs);
    return *this;
  }

  // modifiers
  void emplace() { var_ = std::monostate(); }

  // swap
  template <typename = std::enable_if_t<std::is_swappable_v<E>>>
  void swap(expected &rhs) noexcept(std::is_nothrow_move_constructible_v<E>) {
    var_.swap(rhs.var_);
  }

  // observers
  constexpr explicit operator bool() const noexcept { return has_value(); }
  constexpr bool has_value() const noexcept { return var_.index() == 0; }
  constexpr bool ok() const noexcept { return has_value(); }

  constexpr void value() const & {
    if (!has_value())
      std::get<0>(var_);
  }

  constexpr const E &error() const & { return std::get<unexpected_type>(var_).value(); }
  constexpr E &error() & { return std::get<unexpected_type>(var_).value(); }
  constexpr const E &&error() const && {
    return std::move(std::get<unexpected_type>(var_)).value();
  }
  constexpr E &&error() && { return std::move(std::get<unexpected_type>(var_)).value(); }

  // expected equality operators
  template <class E1, class E2>
  friend constexpr bool operator==(const expected<void, E1> &x, const expected<void, E2> &y);

  // Specialized algorithms
  template <class T1, class E1> friend void swap(expected<T1, E1> &, expected<T1, E1> &) noexcept;

private:
  std::variant<std::monostate, unexpected_type> var_;
};

template <class E1, class E2>
constexpr bool operator==(const expected<void, E1> &x, const expected<void, E2> &y) {
  if (x.has_value() != y.has_value())
    return false;
  if (!x.has_value())
    return x.error() == y.error();
  return true;
}

template <class T1, class E1, class E2>
constexpr bool operator==(const expected<T1, E1> &x, const expected<void, E2> &y) {
  if (x.has_value() != y.has_value())
    return false;
  if (!x.has_value())
    return x.error() == y.error();
  return false;
}

template <class E1, class T2, class E2>
constexpr bool operator==(const expected<void, E1> &x, const expected<T2, E2> &y) {
  if (x.has_value() != y.has_value())
    return false;
  if (!x.has_value())
    return x.error() == y.error();
  return false;
}

template <class E> class unexpected {
public:
  // constructors
  constexpr unexpected(const unexpected &) = default;
  constexpr unexpected(unexpected &&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;

  template <class Err = E _ENABLE_IF(
                std::is_constructible_v<E, Err> &&
                !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
                !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
  // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
  constexpr unexpected(Err &&e) : val_(std::forward<Err>(e)) {}

  template <class U, class... Args _ENABLE_IF(
                         std::is_constructible_v<E, std::initializer_list<U> &, Args...>)>
  constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args &&... args)
      : val_(il, std::forward<Args>(args)...) {}

  template <class Err _ENABLE_IF(
      std::is_constructible_v<E, Err> && !std::is_constructible_v<E, unexpected<Err> &> &&
      !std::is_constructible_v<E, unexpected<Err>> &&
      !std::is_constructible_v<E, const unexpected<Err> &> &&
      !std::is_constructible_v<E, const unexpected<Err>> &&
      !std::is_convertible_v<unexpected<Err> &, E> && !std::is_convertible_v<unexpected<Err>, E> &&
      !std::is_convertible_v<const unexpected<Err> &, E> &&
      !std::is_convertible_v<const unexpected<Err>, E> &&
      std::is_convertible_v<Err, E> /* non-explicit */
      )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr unexpected(const unexpected<Err> &rhs) : val_(rhs.value()) {}

  template <class Err _ENABLE_IF(
      std::is_constructible_v<E, Err> && !std::is_constructible_v<E, unexpected<Err> &> &&
      !std::is_constructible_v<E, unexpected<Err>> &&
      !std::is_constructible_v<E, const unexpected<Err> &> &&
      !std::is_constructible_v<E, const unexpected<Err>> &&
      !std::is_convertible_v<unexpected<Err> &, E> && !std::is_convertible_v<unexpected<Err>, E> &&
      !std::is_convertible_v<const unexpected<Err> &, E> &&
      !std::is_convertible_v<const unexpected<Err>, E> &&
      !std::is_convertible_v<Err, E> /* explicit */
      )>
  constexpr explicit unexpected(const unexpected<Err> &rhs) : val_(E(rhs.value())) {}

  template <class Err _ENABLE_IF(
      std::is_constructible_v<E, Err> && !std::is_constructible_v<E, unexpected<Err> &> &&
      !std::is_constructible_v<E, unexpected<Err>> &&
      !std::is_constructible_v<E, const unexpected<Err> &> &&
      !std::is_constructible_v<E, const unexpected<Err>> &&
      !std::is_convertible_v<unexpected<Err> &, E> && !std::is_convertible_v<unexpected<Err>, E> &&
      !std::is_convertible_v<const unexpected<Err> &, E> &&
      !std::is_convertible_v<const unexpected<Err>, E> &&
      std::is_convertible_v<Err, E> /* non-explicit */
      )>
  // NOLINTNEXTLINE(google-explicit-constructor)
  constexpr unexpected(unexpected<Err> &&rhs) : val_(std::move(rhs.value())) {}

  template <class Err _ENABLE_IF(
      std::is_constructible_v<E, Err> && !std::is_constructible_v<E, unexpected<Err> &> &&
      !std::is_constructible_v<E, unexpected<Err>> &&
      !std::is_constructible_v<E, const unexpected<Err> &> &&
      !std::is_constructible_v<E, const unexpected<Err>> &&
      !std::is_convertible_v<unexpected<Err> &, E> && !std::is_convertible_v<unexpected<Err>, E> &&
      !std::is_convertible_v<const unexpected<Err> &, E> &&
      !std::is_convertible_v<const unexpected<Err>, E> &&
      !std::is_convertible_v<Err, E> /* explicit */
      )>
  constexpr explicit unexpected(unexpected<Err> &&rhs) : val_(E(std::move(rhs.value()))) {}

  // assignment
  constexpr unexpected &operator=(const unexpected &) = default;
  constexpr unexpected &
  operator=(unexpected &&) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
  template <class Err = E> constexpr unexpected &operator=(const unexpected<Err> &rhs) {
    val_ = rhs.value();
    return *this;
  }
  template <class Err = E> constexpr unexpected &operator=(unexpected<Err> &&rhs) {
    val_ = std::forward<E>(rhs.value());
    return *this;
  }

  // observer
  constexpr const E &value() const &noexcept { return val_; }
  constexpr E &value() & noexcept { return val_; }
  constexpr const E &&value() const &&noexcept { return std::move(val_); }
  constexpr E &&value() && noexcept { return std::move(val_); }

  void swap(unexpected &other) noexcept(std::is_nothrow_swappable_v<E>) {
    std::swap(val_, other.val_);
  }

  template <class E1, class E2>
  friend constexpr bool operator==(const unexpected<E1> &e1, const unexpected<E2> &e2);
  template <class E1, class E2>
  friend constexpr bool operator!=(const unexpected<E1> &e1, const unexpected<E2> &e2);

  template <class E1>
  friend void swap(unexpected<E1> &x, unexpected<E1> &y) noexcept(noexcept(x.swap(y)));

private:
  E val_;
};

template <class E1, class E2>
constexpr bool operator==(const unexpected<E1> &e1, const unexpected<E2> &e2) {
  return e1.value() == e2.value();
}

template <class E1, class E2>
constexpr bool operator!=(const unexpected<E1> &e1, const unexpected<E2> &e2) {
  return e1.value() != e2.value();
}

template <class E1> void swap(unexpected<E1> &x, unexpected<E1> &y) noexcept(noexcept(x.swap(y))) {
  x.swap(y);
}

// TODO: bad_expected_access class

#undef _ENABLE_IF
#undef _NODISCARD_

} // namespace base
} // namespace android
